package com.agileai.portal.bizmoduler.sso;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import net.sf.json.JSONObject;

import com.agileai.common.AppConfig;
import com.agileai.hotweb.common.BeanFactory;
import com.agileai.hotweb.common.StringTemplateLoader;
import com.agileai.portal.driver.AttributeKeys;
import com.agileai.portal.portlets.PortletCacheManager;
import com.agileai.util.CryptionUtil;
import com.agileai.util.StringUtil;

import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;

public class SSOTicket {
	private List<SSOTicketEntry> ssoTicketEntryList = new ArrayList<SSOTicketEntry>();
	private String appId = null;
	private String appType = null;
	private String authType = null;
	private String appURL = null;
	private String extURL = null;
	private String userId = null;
	private int timeoutMinutes = 0;
	private String redirectURL = null;
	
	public String getAppId() {
		return appId;
	}
	public void setAppId(String appId) {
		this.appId = appId;
	}
	public String getAppURL() {
		return appURL;
	}
	public void setAppURL(String appURL) {
		this.appURL = appURL;
	}
	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public String getExtURL() {
		return extURL;
	}
	public void setExtURL(String extURL) {
		this.extURL = extURL;
	}
	public List<SSOTicketEntry> getSsoTicketEntryList() {
		return ssoTicketEntryList;
	}
	public int getTimeoutMinutes() {
		return timeoutMinutes;
	}
	public void setTimeoutMinutes(int timeoutMinutes) {
		this.timeoutMinutes = timeoutMinutes;
	}
	public String getAppType() {
		return appType;
	}
	public void setAppType(String appType) {
		this.appType = appType;
	}
	public String getAuthType() {
		return authType;
	}
	public void setAuthType(String authType) {
		this.authType = authType;
	}
	public String getExeInvokeURL(String userId){
		evalSSOTicket(this, userId);
		return buildMergedURL(appURL);
	}
	public String getRedirectURL() {
		return redirectURL;
	}
	public void setRedirectURL(String redirectURL) {
		this.redirectURL = redirectURL;
	}
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private String buildMergedURL(String dataURL){
		String result = dataURL;
		try {
			StringWriter writer = new StringWriter();
			String encoding = "UTF-8";
	    	Configuration cfg = new Configuration();
	    	cfg.setTemplateLoader(new StringTemplateLoader(dataURL));  
	    	cfg.setEncoding(Locale.getDefault(),encoding);
	        cfg.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
	    	Template temp = cfg.getTemplate("");
	    	temp.setEncoding(encoding);
	    	
	        Map root = new HashMap();
	        for (int i=0;i < ssoTicketEntryList.size();i++){
	        	SSOTicketEntry ssoTicketEntry = ssoTicketEntryList.get(i);
	        	String paramCode = ssoTicketEntry.getParamCode();
	        	String paramValue = ssoTicketEntry.getParamValue();
	        	root.put(paramCode,paramValue);	        	
	        }
	        temp.process(root, writer);
	        writer.flush();
	        result = writer.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	
	@SuppressWarnings("rawtypes")
	public static void evalSSOTicket(SSOTicket ssoTicket,String userId){
		List<SSOTicketEntry> ssoTicketEntries = ssoTicket.getSsoTicketEntryList();
		HashMap<String,SSOTicketEntry> unEvalSSsoTicketMap = new HashMap<String,SSOTicketEntry>();
		
		for (int i=0;i < ssoTicketEntries.size();i++){
			SSOTicketEntry ssoTicketEntry = ssoTicketEntries.get(i);
			String valueExpression = ssoTicketEntry.getValueExpression();
			if (!StringUtil.isNullOrEmpty(valueExpression)){
				if (valueExpression.startsWith("${") && valueExpression.endsWith("}")){
					String expression = valueExpression.substring(2,valueExpression.length()-1);
					if (AttributeKeys.CURRENT_USER_ID.equals(expression)){
						ssoTicketEntry.setParamValue(userId);
						tryDecyption(ssoTicketEntry);
					}else{
						unEvalSSsoTicketMap.put(ssoTicketEntry.getParamCode(),ssoTicketEntry);
						tryDecyption(ssoTicketEntry);
					}
				}else{
					ssoTicketEntry.setParamValue(valueExpression);
					tryDecyption(ssoTicketEntry);
				}
			}else{
				tryDecyption(ssoTicketEntry);
			}
		}
		
		String extURL = ssoTicket.getExtURL();
		if (!StringUtil.isNullOrEmpty(extURL) && !unEvalSSsoTicketMap.isEmpty()){
			HashMap variables = new HashMap();
			initVariables(variables, ssoTicketEntries, unEvalSSsoTicketMap);
			StringWriter writer = new StringWriter();
			parseExtURL(variables, extURL, writer);
			String realExtURL = writer.toString();
			
			try {
				String ajaxData = PortletCacheManager.getOnly().getCachedData("N", realExtURL, "1");
				if (!StringUtil.isNullOrEmpty(ajaxData)){
					HashMap<String,String> ticketVars = new HashMap<String,String>();
					JSONObject jsonObject = JSONObject.fromObject(ajaxData);
					Iterator keys = jsonObject.keys();
					while (keys.hasNext()){
						String key = (String)keys.next();
						String value = (String)jsonObject.get(key);
						ticketVars.put(key, value);
					}
					parseUnEvalTicketEntries(unEvalSSsoTicketMap, ticketVars);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	
	private static void parseUnEvalTicketEntries(HashMap<String,SSOTicketEntry> unEvalSSsoTicketMap,HashMap<String,String> ticketVars){
		Iterator<String> keys = unEvalSSsoTicketMap.keySet().iterator();
		while (keys.hasNext()){
			String key = keys.next();
			SSOTicketEntry ssoTicketEntry = unEvalSSsoTicketMap.get(key);
			String valueExpression = ssoTicketEntry.getValueExpression();
			StringWriter writer = new StringWriter();
			parseTicketEntry(ticketVars, valueExpression, writer);
			String evaledParamValue = writer.toString();
			ssoTicketEntry.setParamValue(evaledParamValue);
			
			tryDecyption(ssoTicketEntry);
		}
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static void initVariables(HashMap variables,List<SSOTicketEntry> ssoTicketEntries,HashMap<String,SSOTicketEntry> unEvalSSsoTicketMap){
		for (int i=0;i < ssoTicketEntries.size();i++){
			SSOTicketEntry ssoTicketEntry = ssoTicketEntries.get(i);
			String paramCode = ssoTicketEntry.getParamCode();
			if (unEvalSSsoTicketMap.containsKey(paramCode)){
				continue;
			}
			String paramValue = ssoTicketEntry.getParamValue();
			variables.put(paramCode, paramValue);
		}
	}
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static void parseExtURL(HashMap variables,String template,StringWriter writer) {
		String encoding = "utf-8";
		try {
        	Configuration cfg = new Configuration();
        	cfg.setTemplateLoader(new StringTemplateLoader(template));  
        	cfg.setEncoding(Locale.getDefault(), encoding);
            cfg.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
        	Template temp = cfg.getTemplate("");
        	temp.setEncoding(encoding);
            Map root = new HashMap();
            root.putAll(variables);
            temp.process(root, writer);
            writer.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static void parseTicketEntry(HashMap variables,String template,StringWriter writer) {
		String encoding = "utf-8";
		try {
        	Configuration cfg = new Configuration();
        	cfg.setTemplateLoader(new StringTemplateLoader(template));  
        	cfg.setEncoding(Locale.getDefault(), encoding);
            cfg.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
        	Template temp = cfg.getTemplate("");
        	temp.setEncoding(encoding);
            Map root = new HashMap();
            root.putAll(variables);
            temp.process(root, writer);
            writer.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static void tryDecyption(SSOTicketEntry ssoTicketEntry){
		if (ssoTicketEntry.isEncription()){
			String currentValue = ssoTicketEntry.getParamValue();
			AppConfig appConfig = BeanFactory.instance().getAppConfig();
			String securityKey = appConfig.getConfig("GlobalConfig", "SecurityKey");
			String decyptionValue = CryptionUtil.decryption(currentValue, securityKey);
			ssoTicketEntry.setParamValue(decyptionValue);
		}
	}
}